/**
 *MazePane.java
 *@anthor Daniel Liu
 *2006-5-11 ����10:41:03
 **/
package ui;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.JPanel;
import maze.Maze;
import maze.Mazes;

public class MazePane extends JPanel {
	private int rows = 101;
	private int columns = 101;
	private int[][] m = null;
	private Color[] colors = new Color[7];
	private BufferedImage mazeImage = null;
	private Graphics gi = null;
	private int longInterval = 1500;
	private int delay = 5;
	private Random ran = new Random();
	// edges
	private int canvasWidth = -1; // width of applet, to be set by checkSize()
	private int canvasHeight = -1; // height of applet, to be set by checkSize()
	private int totalWidth; // width of applet, minus border area (set in
							// checkSize())
	private int totalHeight; // height of applet, minus border area (set in

	// checkSize())
	public MazePane() {
		super();
		initialize();
		worker();
	}

	private void initialize() {
		if (0 == rows % 2)
			rows++;
		if (0 == columns % 2)
			columns++;
		colors[Maze.wall] = Color.gray;
		colors[Maze.path] = new Color(0, 128, 0);
		colors[Maze.empty] = Color.black;
		colors[Maze.visited] = new Color(0, 0, 128);
		colors[Maze.entry] = Color.green;
		colors[Maze.exit] = Color.red;
		colors[Maze.background] = Color.black;
		this.setBackground(colors[Maze.background]);

	}

	private void worker() {
		new Thread(new Runnable() {
			public void run() {
				try {
					Point entry = new Point();
					Point exit = new Point();
					int[][] maze = new int[rows][columns];
					while (true) {
						Thread.sleep(longInterval);
						Mazes.newMaze(maze);
						m = maze;
						drawMaze(maze);
						randomCell(maze, entry);
						randomCell(maze, exit);
						drawCell(gi, exit.x, exit.y, Maze.exit);
						recursiveSolveMaze(maze, entry, exit);
						repaint();
					}
				} catch (InterruptedException e) {
				}
			}
		}).start();
	}

	private Point randomCell(int[][] maze, Point p) {
		p.x = ran.nextInt(columns);
		p.y = ran.nextInt(rows);
		if (Maze.empty != maze[p.x][p.y])
			return randomCell(maze, p);
		return p;
	}

	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		g.drawImage(this.getMazeImage(), 0, 0, this);
	}

	private BufferedImage getMazeImage() {
		this.adjustImageSize();
		return this.mazeImage;
	}

	private boolean recursiveSolveMaze(int[][] maze, Point start, Point exit) {
		this.repaint();
		int row = start.x;
		int col = start.y;
		if (maze[row][col] == Maze.empty) {
			maze[row][col] = Maze.path; // add this cell to the path
			drawCell(gi, row, col, Maze.path);
			if (row == exit.x && col == exit.y)
				return true; // path has reached goal
			try {
				Thread.sleep(delay);
				// try to solve maze by extending path in each possible
				// direction
				if (recursiveSolveMaze(maze, new Point(start.x - 1, start.y), exit)
						|| recursiveSolveMaze(maze, new Point(start.x, start.y - 1), exit)
						|| recursiveSolveMaze(maze, new Point(start.x + 1, start.y), exit)
						|| recursiveSolveMaze(maze, new Point(start.x, start.y + 1), exit))
					return true;
				// maze can't be solved from this cell, so backtract out of the
				// cell
				// mark cell as having been visited
				maze[row][col] = Maze.visited;
				drawCell(gi, row, col, Maze.visited);
				Thread.sleep(delay);
			} catch (InterruptedException e) {
			}
		}
		return false;
	}

	// private void dfsSolveMaze( Point entry, Point exit )
	// {
	// }

	private void drawCell(Graphics g, int row, int col, int color) {
		int w = totalWidth / columns; // width of each cell
		int h = totalHeight / rows; // height of each cell
		g.setColor(colors[color]);
		g.fillRect(col * w, row * h, w, h);
	}

	private boolean adjustImageSize() {
		int newWidth = this.getWidth(), newHeight = this.getHeight();
		if (newWidth != canvasWidth || newHeight != canvasHeight) {
			canvasWidth = newWidth;
			canvasHeight = newHeight;
			int w = (canvasWidth) / columns;
			int h = (canvasHeight) / rows;
			totalWidth = w * columns;
			totalHeight = h * rows;
			this.mazeImage = new BufferedImage(totalWidth, totalHeight, BufferedImage.TYPE_INT_RGB);
			this.gi = this.mazeImage.createGraphics();
			this.drawMaze(m);
			return true;
		}
		return false;
	}

	private void drawMaze(int[][] maze) {
		if (null == maze)
			maze = Mazes.newMaze(rows, columns);
		gi.setColor(colors[Maze.background]);
		gi.fillRect(0, 0, canvasWidth, canvasHeight);
		int x = 0, y = 0, cx = 0, cy = 0;
		int cellWidth = totalWidth / columns; // width of each cell
		int cellHeight = totalHeight / rows; // height of each cell
		for (int row = 0; row < rows; row++)
			for (int col = 0; col < columns; col++) {
				x = col * cellWidth;
				y = row * cellHeight;
				gi.setColor(colors[maze[row][col]]);
				if (maze[row][col] == Maze.wall) {
					// top
					cx = x + cellWidth / 2;
					cy = y + cellHeight / 2;
					if (0 != row && Maze.wall == maze[row - 1][col])
						gi.drawLine(cx, cy, cx, cy - cellHeight / 2);
					// left
					if (0 != col && Maze.wall == maze[row][col - 1])
						gi.drawLine(cx, cy, cx - cellWidth / 2, cy);
					// bottom
					if (rows - 1 != row && Maze.wall == maze[row + 1][col])
						gi.drawLine(cx, cy, cx, cy + cellHeight / 2);
					// right
					if (columns - 1 != col && Maze.wall == maze[row][col + 1])
						gi.drawLine(cx, cy, cx + cellWidth / 2, cy);
				} else
					gi.fillRect(x, y, cellWidth, cellHeight);
			}
	}
}
